﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IndexedList
{
    public class IndexCollection<T> : ICollection<IListIndex<T>> where T : new()
    {
        Dictionary<int, IListIndex<T>> collection = new Dictionary<int, IListIndex<T>>();

        public void Add(IListIndex<T> item)
        {
            if (!this.Contains(item))
            {
                int indexHash = GetFieldsHash(item.IndexFields.Keys);
                collection.Add(indexHash, item);
            }
        }

        public void Clear()
        {
            collection.Clear();
        }

        public bool Contains(IListIndex<T> item)
        {
            int indexHash = GetFieldsHash(item.IndexFields.Keys);
            if (!Contains(indexHash))
            {
                if (!Contains(item.Name))
                {
                    return false;
                }
            }
            return true;
        }

        private bool Contains(int indexHash)
        {
            return collection.ContainsKey(indexHash);
        }

        private bool Contains(string name)
        {
            return this.collection.Values.Any(p => p.Name.Equals(name));
        }

        public void CopyTo(IListIndex<T>[] array, int arrayIndex)
        {
            collection.Values.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get
            {
                return collection.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public bool Remove(IListIndex<T> item)
        {
            int indexHash = GetFieldsHash(item.IndexFields.Keys);
            if (this.Contains(indexHash))
                return collection.Remove(indexHash);
            return false;
        }

        public bool Remove(string name)
        {
            IListIndex<T> index = GetIndexByName(name);
            if (index != null)
                return Remove(index);
            return false;
        }

        public IEnumerator<IListIndex<T>> GetEnumerator()
        {
            return collection.Values.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return collection.Values.GetEnumerator();
        }

        public IListIndex<T> this[string name]
        {
            get
            {
                return GetIndexByName(name);
            }
        }

        public IListIndex<T> GetIndexByName(string name)
        {
            return this.collection.Values.Where(p => p.Name == name).FirstOrDefault();
        }

        public IListIndex<T> GetIndexByFields(IEnumerable<string> fields)
        {
            int fieldsHash = GetFieldsHash(fields);
            if (this.Contains(fieldsHash))
            {
                return collection[fieldsHash];
            }
            return null;
        }

        public IListIndex<T> GetIndexByFieldValues(IEnumerable<object> fieldvalues)
        {
            IListIndex<T> index = null;
            foreach (var item in this)
            {
                int i = 0;
                if (fieldvalues.Count() == item.IndexFields.Count)
                {
                    bool found = true;
                    List<object> valueslist = fieldvalues.ToList();
                    foreach (var ftype in item.IndexFields.Values)
                    {
                        if (valueslist[i] == null)
                            //throw new Exception("Cannot find index with NULL value field.");
                            continue;
                        
                        Type fieldtype = ftype.PropertyType;
                        if (fieldtype.IsGenericType && fieldtype.GetGenericTypeDefinition() == typeof(Nullable<>))
                        {
                            fieldtype = Nullable.GetUnderlyingType(ftype.PropertyType);
                        }
                        if (!valueslist[i].GetType().Name.Equals(fieldtype.Name))
                        {
                            found = false;
                            break;
                        }
                        i++;
                    }
                    if (found)
                    {
                        if (index != null)
                            throw new Exception("Cannot find suitable index, there are 2 or more indexes witch are match with the values you supplied, please specify indexname.");
                        else
                            index = item;
                    }
                }
            }
            return index;
        }

        int GetFieldsHash(IEnumerable<string> fields)
        {
            int res = 0;
            foreach (var str in fields)
            {
                res ^= str.GetHashCode();
            }
            return res;
        }

    }
}
